In [15]:
import pandas as pd
import altair as alt
Fundamentos de Programación en Python para las Ciencias Sociales¶
In [16]:
##El estudio de los conflictos sociales permite identificar las tensiones entre el Estado y la sociedad civil. Este reporte analiza una base de datos de eventos de protesta (2023-2024), buscando no solo describir su frecuencia, sino también su intensidad y el tratamiento informativo que reciben.
Objetivo: Aplicar herramientas de programación en Python para limpiar, transformar y visualizar patrones de protesta y su impacto en la agenda pública.¶
In [17]:
ruta = r"C:\Users\maria\Downloads\Python -QLAB\Base de Eventos de Protesta del Perú_1980_2025.xlsx"
data = pd.read_excel(ruta)
data.columns = data.columns.str.lower()
data['número_heridos'] = pd.to_numeric(data['número_heridos'], errors='coerce').fillna(0)
data['número_muertos'] = pd.to_numeric(data['número_muertos'], errors='coerce').fillna(0)
data.head()
Out[17]:
| id | periódico | periódico_id | día | mes_id | mes | año | fecha | presidente_id | presidente | ... | institución_1 | institución_2 | institución_3 | institución_id | reclamo_t | reclamo | reclamo_id | sub_reclamo | sub_reclamo_id | comentar_t | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Expreso | 5.0 | 1 | 1 | Enero | 1980 | ############ | 1 | Morales Bermúdez | ... | Empresas periodísticas | Periodicos y diarios. | NaN | 909.0 | Exigen que las empresas periodísticas les otor... | Laborales | 1 | Bonificaciones | 105 | NaN |
| 1 | 2 | Expreso | 5.0 | 3 | 1 | Enero | 1980 | ############ | 1 | Morales Bermúdez | ... | Instituto Peruano de Seguridad Social | NaN | NaN | 712.0 | Demandan una mejora en condiciones salariales. | Laborales | 1 | Aumentos salariales | 101 | Exigen además: recategorización, aumento gener... |
| 2 | 3 | Expreso | 5.0 | 4 | 1 | Enero | 1980 | ############ | 1 | Morales Bermúdez | ... | Ministerio de Justicia | Centro de Readaptación Social de Chorrillos | NaN | 105.0 | Protestan por la mala alimentación recibida en... | Servicios | 4 | Alimentación | 407 | 15 internas resultaron heridas. |
| 3 | 4 | Expreso / El Comercio | 6.0 | 4 | 1 | Enero | 1980 | ############ | 1 | Morales Bermúdez | ... | Empresas periodísticas | Empresa Periodistica Nacional S.A. | NaN | 909.0 | Protestan contra medidas de la empresa que ate... | Laborales | 1 | Mejores condiciones laborales | 104 | Huelga declarada ilegal por el gobierno. Inici... |
| 4 | 5 | El Comercio | 7.0 | 4 | 1 | Enero | 1980 | ############ | 1 | Morales Bermúdez | ... | Municipalidades Provinciales de Cusco | Cusco | NaN | 508.0 | Denuncian el despido arbitrario de trabajadores. | Laborales | 1 | Estabilidad laboral | 109 | También demandan la destitución de cuatro func... |
5 rows × 51 columns
In [18]:
variables_interes = ['año', 'región', 'número_heridos', 'número_muertos', 'sector_1']
data_estudio = data[variables_interes].copy()
data_estudio['número_heridos'] = data_estudio['número_heridos'].fillna(0)
data_estudio['número_muertos'] = data_estudio['número_muertos'].fillna(0)
In [19]:
def categorizar_protesta(fila):
if fila == 0:
return "Pacífica/Sin víctimas"
elif fila <= 5:
return "Violencia Moderada"
else:
return "Violencia Alta"
data_estudio['total_victimas'] = data_estudio['número_heridos'] + data_estudio['número_muertos']
data_estudio['intensidad'] = data_estudio['total_victimas'].apply(categorizar_protesta)
total_victimas intensidad 0 0.0 Pacífica/Sin víctimas 1 0.0 Pacífica/Sin víctimas 2 15.0 Violencia Alta 3 0.0 Pacífica/Sin víctimas 4 0.0 Pacífica/Sin víctimas
In [20]:
grafico_1 = alt.Chart(data_estudio).mark_line(point=True).encode(
x = alt.X('año:O', title='Año'),
y = alt.Y('count():Q', title='Cantidad de Protestas'),
tooltip = ['año', 'count()']
).properties(title='Evolución de las Protestas en el Perú', width=500).interactive()
In [21]:
import altair as alt
alt.data_transformers.disable_max_rows()
Out[21]:
DataTransformerRegistry.enable('default')
In [22]:
grafico_1.display()
In [43]:
dataregiones = data_estudio['región'].value_counts().head(24).index.tolist()
data_regiones = data_estudio[data_estudio['región'].isin(dataregiones)]
grafico_2 = alt.Chart(data_regiones).mark_bar().encode(
x = alt.X('count():Q', title='Número de Eventos'),
y = alt.Y('región:N', sort='-x', title='Departamento'),
color = alt.Color('intensidad:N', title='Nivel de Intensidad'),
tooltip = ['región', 'intensidad', 'count()']
).properties(title='Intensidad de Protestas en Regiones', width=500).interactive()
In [44]:
grafico_2.display()
In [25]:
variables_interes1 = ['año', 'región', 'periódico', 'número_muertos', 'sector_1']
data_estudio1 = data[variables_interes1].copy()
tabla = pd.crosstab(data_estudio1['periódico'], data_estudio1['sector_1'], normalize='index') * 100
In [26]:
data_periodicos = data_estudio1.assign(periódico=data_estudio1['periódico'].str.split('/'))
data_periodicos = data_periodicos.explode('periódico')
data_periodicos['periódico'] = data_periodicos['periódico'].str.strip()
In [27]:
tabla = pd.crosstab(data_periodicos['sector_1'], data_periodicos['periódico'], normalize='columns') * 100
tabla_altair = tabla.reset_index()
tabla_melted = tabla_altair.melt(id_vars='sector_1', var_name='periódico', value_name='porcentaje')
In [29]:
grafico_final = alt.Chart(tabla_melted).mark_bar().encode(
x = alt.X('porcentaje:Q', title='Porcentaje de Cobertura (%)'),
y = alt.Y('sector_1:N', sort='-x', title='Sector de la Protesta'),
color = alt.Color('periódico:N', title='Periódico'),
tooltip = ['periódico', 'sector_1', 'porcentaje']
).properties(
title='Agenda Mediática: Distribución de Sectores por Periódico',
width=600,
height=400
).interactive()
grafico_final.display()